#include "clib_epoll.h"
#include "clib_stack.h"
#include "clib_sock.h"
#include "clib_tcp_buffer.h"
#include "clib_net_def.h"
#include "clib_stack_malloc.h"

#include <errno.h>

i32_t
clib_epoll_create(i32_t size) 
{
    i32_t socket_id;
    clib_epoll_t *epoll;
    
    i32_t idx = clib_stack_index_get();
    stack_ctx_t *ctx  = &l4_satck.ctx[idx];
    clib_sock_t *sock = clib_get_sock_new_by_stack(ctx);
    socket_id = ctx->socket_id;

    if(!sock) {
        errno = EBADF;
        return -1;
    }

    sock->sock_type = socket_type_epoll;
    epoll = &sock->epoll;
    epoll->backlog  = size;
    epoll->sock     = sock;
    /** TODO **/
    epoll->ev_ring = clib_stack_malloc(NULL, sizeof(clib_ring_t) + 65536 * (sizeof(void*)), CLIB_ALIGN_SIZE, socket_id);
    
    epoll->sh_ring = clib_stack_malloc(NULL, sizeof(clib_ring_t) + 65536 * (sizeof(void*)), CLIB_ALIGN_SIZE, socket_id);
    
    if(!epoll->ev_ring || !epoll->sh_ring) {
        /** TODO clean epoll **/
        clib_put_sock_to_stack(ctx, sock);
        errno = EBADF;
        return -1;
    }

    return sock->sock_id;
}

i32_t
clib_epoll_ctl(i32_t efd, i32_t opt, i32_t sfd, clib_ep_event_t *event) 
{
    clib_sock_t *sock;
    clib_sock_t *ep;
    tcp_stream_t *stream;
    i32_t idx;
    stack_ctx_t *ctx;
    /** TODO UDP **/
    idx  = clib_stack_index_get();
    ctx  = &l4_satck.ctx[idx];
    sock = clib_get_sock_by_id(ctx, sfd);
    ep   = clib_get_sock_by_id(ctx, efd);

    if(!sock || !ep) {
        errno = EBADF;
        return -1;
    }
    
    if(opt == CLIB_EPOLL_CTL_ADD) {
        sock->ep = ep;
        sock->ev = *event;
        sock->ev.events |= (CLIB_EPOLLERR | CLIB_EPOLLHUP);
    } else if(opt == CLIB_EPOLL_CTL_DEL) {
        sock->ev.events   = CLIB_EPOLLNONE;
    } else if(opt == CLIB_EPOLL_CTL_MOD) {
        sock->ep = ep;
        sock->ev = *event;
        sock->ev.events |= (CLIB_EPOLLERR | CLIB_EPOLLHUP);
    }
    
    if(sock->ev.events & CLIB_EPOLLIN) {
        if(sock->sock_type == socket_type_sock_tcp) {
            stream = &sock->tcp_stream;
            if(stream->recv_buffer && stream->recv_buffer->ready_len > 0) {
                clib_epoll_update_event(ep, sock, CLIB_EPOLL_EVENT_DEAL_WITH, CLIB_EPOLLIN);
            }
        } else if(sock->sock_type == socket_type_listen) {
            /** TODO lister 事件判断 **/
            clib_epoll_update_event(ep, sock, CLIB_EPOLL_EVENT_DEAL_WITH, CLIB_EPOLLIN);
        } else if(sock->sock_type == socket_type_sock_udp) {

        }
    } 
    
    if(sock->ev.events & CLIB_EPOLLOUT) {
        sock->ev.events |= (CLIB_EPOLLERR | CLIB_EPOLLHUP);
        clib_epoll_update_event(ep, sock, CLIB_EPOLL_EVENT_DEAL_WITH, CLIB_EPOLLOUT);
    }

    return rn_ok;
}

i32_t
clib_epoll_wait(i32_t efd, clib_ep_event_t **events, i32_t max_events, int timeout) 
{
    i32_t event_n;
    clib_sock_t *ep;
    i32_t idx = clib_stack_index_get();
    stack_ctx_t *ctx  = &l4_satck.ctx[idx];
    ep   = clib_get_sock_by_id(ctx, efd);

    if(!ep || ep->sock_type != socket_type_epoll) {
        errno = EBADF;
        return -1;
    }

    if(!timeout) {
        // if(ep->epoll.ev_idx < max_events) {
        //     event_n = ep->epoll.ev_idx;
        // } else {
        //     event_n = max_events;
        // }
        // for(i32_t i = 0; i < event_n; i++) {
        //     events[i] = ep->epoll.ev_queue[i]; 
        // }
    
        
    } else {
        /** TODO timeout > 0 的情况处理 **/
    }
    return event_n;
}

i32_t
clib_epoll_update_event(clib_epoll_t* ep, clib_sock_t *sock, i32_t type, u32_t event)
{
    if(sock->ev.events & event) {

        /** TODO socket lock **/

        if(type == CLIB_EPOLL_EVENT_DEAL_WITH) {
            if(sock->on_ep_ring) {
                if(!(sock->event & event)) {
                    if(clib_ring_enqueue_elem(ep->ev_ring, sock)) {
                        return -1;
                    }
                    sock->event |= event;
                    ep->event_post++;
                }
            } else {
                if(clib_ring_enqueue_elem(ep->ev_ring, sock)) {
                    return -1;
                }
                sock->on_ep_ring = 1;
                sock->event |= event;
                ep->event_post++;
            }
        } else {
            if(!sock->on_sh_ring) {
                if(clib_ring_enqueue_elem(ep->sh_ring, sock)) {
                    return -1;
                }
                sock->on_sh_ring = 1;
                ep->event_post++;
            }
            
        }
        return 0;
    }

    return -1;
}